package tpase.GlobalServer.Handler;

import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import tpase.GlobalServer.DO.*;
import tpase.GlobalServer.VO.Message;
import tpase.GlobalServer.VO.SetupParam;
import tpase.GlobalServer.VO.StatusCode;
import tpase.util.Util;

import java.util.List;
import java.util.Map;
import java.util.Set;



@ChannelHandler.Sharable
public class SetupNettyServerHandler extends ChannelInboundHandlerAdapter {
    private static String paramPath = "src/main/resources/config/parameters.json";
    private List<Share> shares;
    private Set<Address> serverSet;
    List<Address> serverList;
    private int idx;
    private int t, n;
    private PublicParameter publicParameter;
    private List<GroupGenerator> groupGeneratorList;
    private List<PublicCommitment> publicCommitmentList;


    public SetupNettyServerHandler() {
    }

    public SetupNettyServerHandler(PublicParameter publicParameter, List<GroupGenerator> groupGeneratorList, List<PublicCommitment> publicCommitmentList, List<Share> shares, Set<Address> serverSet, List<Address> serverList) {
        this.publicParameter = publicParameter;
        this.groupGeneratorList = groupGeneratorList;
        this.publicCommitmentList = publicCommitmentList;

        this.shares = shares;
        this.serverSet = serverSet;
        this.serverList = serverList;
        idx = serverSet.size();
        SetupParam setupParam = Util.jsonFileToObj(paramPath, SetupParam.class);
        t = setupParam.getT();
        n = setupParam.getN();
    }

    @Override
    public synchronized void channelRead(ChannelHandlerContext ctx, Object msg) {
        Message request = (Message) msg;
        int nowStatusCode = request.getStatusCode();

        if (nowStatusCode == StatusCode.SETUP_REQUEST) {
            setupResponse(ctx);
        } else if (nowStatusCode == StatusCode.PUBLIC_PARAM_REQUEST) {
            publicParamResponse(ctx);
        } else {
            ctx.close();
        }
    }


    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {

    }

    private void publicParamResponse(ChannelHandlerContext ctx) {
        Message message = new Message(StatusCode.PUBLIC_PARAM_RESPONSE);
        Map<String, byte[]> map = message.getMap();

        setMap(map);

        map.remove("x");
        map.remove("idx");
        String s = Util.toJson(serverList);
        map.put("serverList", s.getBytes());

        ctx.writeAndFlush(message);

        ctx.close();
    }

    private void setMap(Map<String,byte[]> map) {
        for (GroupGenerator groupGenerator : groupGeneratorList) {
            map.put(groupGenerator.getName(), groupGenerator.getValue());
        }
        for (PublicCommitment publicCommitment : publicCommitmentList) {
            if ("-1".equals(publicCommitment.getKeyServerId())) {
                map.put("gamma_s", publicCommitment.getGamma_1_si());
                continue;
            }
            map.put("gamma_s" + publicCommitment.getKeyServerId(), publicCommitment.getGamma_1_si());
            map.put("gamma_ts" + publicCommitment.getKeyServerId(), publicCommitment.getGamma_t_si());
        }
        map.put("n", Util.intToByteArr(n));
        map.put("t", Util.intToByteArr(t));
    }

    private synchronized void setupResponse(ChannelHandlerContext ctx) {
        idx = serverSet.size();
        if (idx >= n) {
            System.out.println("The number of servers exceeds n for setup");
            return;
        }
        String[] parts = ctx.channel().remoteAddress().toString().split(":");
        String ip = parts[0].substring(1);
        int port = Integer.parseInt(parts[1]);
        Address address = new Address(ip, port);
        // set x
        address.setId(shares.get(idx).getX().toBytes());

        if (serverSet.contains(address)) {
            System.out.println(address.toString() + " has completed setup");
            return;
        }

        Message respond = new Message(StatusCode.SETUP_RESPONSE);
        Map<String, byte[]> map = respond.getMap();
        setMap(map);

        map.put("idx", Util.intToByteArr(idx));
        map.put("s" + idx, shares.get(idx).getY().toBytes());
        map.put("x", shares.get(idx).getX().toBytes());


        serverSet.add(address);
        serverList.add(address);

        ctx.writeAndFlush(respond);
        ctx.close();
    }


    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        System.out.println(cause.toString());
        System.out.println("exceptionCaught: 发生异常，关闭通道");
        // 发生异常，关闭通道
        ctx.close();
    }
}
